import os
import random
import re
import secrets
import string
from datetime import datetime, timedelta
from flask import Flask, request, jsonify, Blueprint
from flask_jwt_extended import create_access_token, get_jwt_identity, JWTManager, jwt_required
from flask_mail import Mail, Message
from flask_sqlalchemy import SQLAlchemy

# 初始化 Flask 应用
app = Flask(__name__)

jwt = JWTManager(app)

# 配置
class Config:
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:123456@localhost:3306/login'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    MAIL_SERVER = 'smtp.qq.com'
    MAIL_PORT = 465
    MAIL_USE_SSL = True
    MAIL_USERNAME = 'travel_together@qq.com'  # 替换为你的QQ邮箱
    MAIL_PASSWORD = 'gmdzpjicmhpodjbd'  # 替换为你的QQ邮箱授权码
    SECRET_KEY = os.urandom(24)
    JWT_SECRET_KEY = secrets.token_urlsafe(64)  # 替换为你的JWT密钥
    JSON_AS_ASCII = False

app.config.from_object(Config)

db = SQLAlchemy(app)

mail = Mail(app)

# 数据库模型
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)  # 直接存储明文密码
    is_active = db.Column(db.Boolean, default=True)  # 默认激活状态

    def __repr__(self):
        return f'<User {self.username}>'

class VerificationCode(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), nullable=False)
    code = db.Column(db.String(6), nullable=False)
    type = db.Column(db.String(20), nullable=False)
    expires_at = db.Column(db.DateTime, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.now)

# 路由
bp = Blueprint('auth', __name__)

# 辅助函数：验证验证码
def verify_code(email, code, code_type):
    verification_code = VerificationCode.query.filter_by(email=email, type=code_type).order_by(VerificationCode.created_at.desc()).first()
    if verification_code is None:
        return False, "验证码无效"
    if verification_code.code != code:
        return False, "验证码无效"
    if verification_code.expires_at < datetime.now():
        return False, "验证码已过期"
    return True, "验证码有效"

@bp.route('/user/register', methods=['POST'])
def register():
    try:
        data = request.json
        username = data.get('username')
        email = data.get('email')
        password = data.get('password')  # 直接存储用户设置的密码
        code = data.get('code')  # 验证码（可选）

        # 校验必填字段
        if not username or not email or not password:
            return jsonify({'code': 400, 'message': '缺少必填字段'}), 400

        # 校验邮箱格式
        if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
            return jsonify({'code': 400, 'message': '邮箱格式不正确'}), 400

        # 检查邮箱/用户名是否已存在
        if User.query.filter_by(email=email).first() or User.query.filter_by(username=username).first():
            return jsonify({'code': 409, 'message': '邮箱或用户名已存在'}), 409

        # 验证验证码（如果需要）
        if code:
            is_valid, message = verify_code(email, code, 'register')
            if not is_valid:
                return jsonify({'code': 400, 'message': message}), 400

        # 创建新用户并直接存储密码
        new_user = User(username=username, email=email, password=password, is_active=True)
        db.session.add(new_user)
        db.session.commit()

        return jsonify({'message': '用户注册成功。'})
    except Exception as e:
        return jsonify({'code': 500, 'message': f'服务器内部错误：{str(e)}'}), 500

@bp.route('/user/login', methods=['POST'])
def login():
    try:
        data = request.json
        identifier = data.get('identifier')
        password = data.get('password')

        # 根据邮箱/用户名查询用户
        user = User.query.filter((User.email == identifier) | (User.username == identifier)).first()
        if not user or user.password != password:  # 直接比较明文密码
            return jsonify({'code': 401, 'message': '用户名/邮箱或密码不匹配'}), 401

        # 生成并返回JWT令牌
        access_token = create_access_token(identity=str(user.id))
        return jsonify({'message': '登录成功！', 'access_token': access_token})
    except Exception as e:
        return jsonify({'code': 500, 'message': f'服务器内部错误：{str(e)}'}), 500

@bp.route('/user/send-code', methods=['POST'])
def send_verify_code():
    try:
        data = request.json
        email = data.get('email')
        code_type = data.get('type')

        # 校验邮箱格式
        if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
            return jsonify({'code': 400, 'message': '邮箱格式不正确'}), 400

        # 生成6位随机数字验证码
        code = ''.join(random.choices(string.digits, k=6))

        # 保存验证码到数据库
        verification_code = VerificationCode(email=email, code=code, type=code_type, expires_at=datetime.now() + timedelta(minutes=5))
        db.session.add(verification_code)
        db.session.commit()

        # 调用邮件服务发送验证码
        msg = Message('验证码', sender=app.config['MAIL_USERNAME'], recipients=[email])
        msg.body = f'您的验证码是 {code}'
        mail.send(msg)

        return jsonify({'message': '验证码已成功发送！'})
    except Exception as e:
        return jsonify({'code': 500, 'message': f'服务器内部错误：{str(e)}'}), 500

@bp.route('/user/verify-code', methods=['POST'])
def verify_code_route():
    try:
        data = request.json
        email = data.get('email')
        code = data.get('code')
        code_type = data.get('type')

        is_valid, message = verify_code(email, code, code_type)
        if not is_valid:
            return jsonify({'code': 400, 'message': message}), 400

        return jsonify({'message': '验证码有效！'})
    except Exception as e:
        return jsonify({'code': 500, 'message': f'服务器内部错误：{str(e)}'}), 500

@bp.route('/user/update-password', methods=['POST'])
@jwt_required()
def update_password():
    try:
        data = request.json
        email = data.get('email')
        new_password = data.get('newPassword')
        code = data.get('code')  # 验证码（可选）
        old_password = data.get('oldPassword')  # 原密码（可选）

        if code:  # 场景1：通过验证码重置密码
            is_valid, message = verify_code(email, code, 'reset')
            if not is_valid:
                return jsonify({'code': 400, 'message': message}), 400

            user = User.query.filter_by(email=email).first()
            if not user:
                return jsonify({'code': 404, 'message': '用户未找到'}), 404

            # 更新密码
            user.password = new_password
            db.session.commit()
            return jsonify({'message': '密码更新成功！'})

        elif old_password:  # 场景2：登录状态下修改密码
            user_id = get_jwt_identity()
            user = User.query.get(user_id)
            if not user or user.password != old_password:  # 直接比较明文密码
                return jsonify({'code': 401, 'message': '原密码不正确'}), 401

            # 更新密码
            user.password = new_password
            db.session.commit()
            return jsonify({'message': '密码更新成功！'})

        else:
            return jsonify({'code': 400, 'message': '缺少必填字段'}), 400
    except Exception as e:
        return jsonify({'code': 500, 'message': f'服务器内部错误：{str(e)}'}), 500

# 注册蓝图
app.register_blueprint(bp, url_prefix='/')

# 创建数据库表
with app.app_context():
    db.create_all()

# 运行 Flask 应用
if __name__ == '__main__':
    app.run(debug=True)